package com.xizi.lotterySystem.service.Impl;

import com.xizi.lotterySystem.common.exception.ServiceException;
import com.xizi.lotterySystem.common.exception.errorCode.ServiceErrorCodeConstants;
import com.xizi.lotterySystem.controller.param.DrawPrizeParam;
import com.xizi.lotterySystem.controller.param.ShowWinningRecordsParam;
import com.xizi.lotterySystem.dao.dataobject.*;
import com.xizi.lotterySystem.dao.mapper.*;
import com.xizi.lotterySystem.service.IDrawPrizeService;
import com.xizi.lotterySystem.service.dto.*;
import com.xizi.lotterySystem.service.enumerate.ActivityPrizeStatusEnum;
import com.xizi.lotterySystem.service.enumerate.ActivityPrizeTiersEnum;
import com.xizi.lotterySystem.service.enumerate.ActivityStatusEnum;
import com.xizi.lotterySystem.utils.JacksonUtil;
import com.xizi.lotterySystem.utils.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.xizi.lotterySystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.xizi.lotterySystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements IDrawPrizeService {
    private static Logger log = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private static final Long PRIZE_RECORD_EFFECTIVE_TIME = 3 * 24 * 60 * 60 * 1000L;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String,String> map = new HashMap<>();
        String UUId = UUID.randomUUID().toString();
        map.put("id",UUId);
        map.put("data", JacksonUtil.writeValueAsString(param));
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
        log.info("加入rabbitMq 成功！param{}", JacksonUtil.writeValueAsString(param));
    }







    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        log.info("param:{}",JacksonUtil.writeValueAsString(param));

        if(param == null || param.getActivityId() == null || param.getPrizeId() == null) {
            log.error(ServiceErrorCodeConstants.PARAM_ERROR.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.PARAM_ERROR);
        }

        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPID(param.getActivityId(),param.getPrizeId());

        if(activityDO == null || activityPrizeDO == null) {
            log.error(ServiceErrorCodeConstants.ACTIVITY_OR_ACTIVITY_PRIZE_ISNULL.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_ACTIVITY_PRIZE_ISNULL);
        }
        //检查活动状态
        if(activityDO.getStatus().name().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            log.error(ServiceErrorCodeConstants.ACTIVITY_FINISHED.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_FINISHED);
        }
        // 检查活动奖品状态
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            log.error(ServiceErrorCodeConstants.ACTIVITY_PRIZE_FINISHED.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_FINISHED);
        }

        // 检查中奖人数 与 奖品数量 是否相等
        if (param.getWinnerList().size() != activityPrizeDO.getPrizeAmount()){
            log.error(ServiceErrorCodeConstants.ACTIVITY_PRIZE_WINNER_COUNT_ERROR.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_WINNER_COUNT_ERROR);
        }

        log.info("checkDrawPrizeParam ()   校验参数完成！");
        return true;
    }



//    @Override
//    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
//        log.info("param:{}",JacksonUtil.writeValueAsString(param));
//
//        if(param == null || param.getActivityId() == null || param.getPrizeId() == null) {
//            log.error(ServiceErrorCodeConstants.PARAM_ERROR.getMessage());
//            return false;
////            throw new ServiceException(ServiceErrorCodeConstants.PARAM_ERROR);
//        }
//
//        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
//        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPID(param.getActivityId(),param.getPrizeId());
//
//        if(activityDO == null || activityPrizeDO == null) {
//            log.error(ServiceErrorCodeConstants.ACTIVITY_OR_ACTIVITY_PRIZE_ISNULL.getMessage());
//            return false;
////            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_ACTIVITY_PRIZE_ISNULL);
//        }
//        //检查活动状态
//        if(activityDO.getStatus().name().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
//            log.error(ServiceErrorCodeConstants.ACTIVITY_FINISHED.getMessage());
//            return false;
////            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_FINISHED);
//        }
//        // 检查活动奖品状态
//        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
//            log.error(ServiceErrorCodeConstants.ACTIVITY_PRIZE_FINISHED.getMessage());
//            return false;
////            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_FINISHED);
//        }
//
//        // 检查中奖人数 与 奖品数量 是否相等
//        if (param.getWinnerList().size() != activityPrizeDO.getPrizeAmount()){
//            log.error(ServiceErrorCodeConstants.ACTIVITY_PRIZE_WINNER_COUNT_ERROR.getMessage());
//            return false;
////            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_WINNER_COUNT_ERROR);
//        }
//
//        log.info("checkDrawPrizeParam ()   校验参数完成！");
//        return true;
//    }





















    @Override
    public List<WinningRecordDO> saveWinningRecords(DrawPrizeParam param) {

        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());

        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());

        List<Long> ids = param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList());
        List<UserDO> userDOList = userMapper.selectByIds(ids);

        List<WinningRecordDO> winningRecordDOList = userDOList
                .stream()
                .map(u -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(ActivityPrizeTiersEnum.forTiers(param.getPrizeTiers()));
                    winningRecordDO.setWinnerId(u.getId());
                    winningRecordDO.setWinnerName(u.getUserName());
                    winningRecordDO.setWinnerEmail(u.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(u.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());
                    return winningRecordDO;
                })
                .collect(Collectors.toList());
        winningRecordMapper.bacthInsert(winningRecordDOList);

        // 3. 将中奖结果存⼊ redis
        saveRecordsToCache(param.getActivityId() + "_" + param.getPrizeId(),
                winningRecordDOList,
                PRIZE_RECORD_EFFECTIVE_TIME);
        // 4. 如果活动已完成，整个活动下的记录统⼀存储⼀遍
        if(activityDO.getStatus().name().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            List<WinningRecordDO> winningAll = winningRecordMapper.selectByActivityId(param.getActivityId());

            saveRecordsToCache(String.valueOf(param.getActivityId()),winningAll,PRIZE_RECORD_EFFECTIVE_TIME);
        }

        return winningRecordDOList;
    }

    @Override
    public int selectWinningRecordsCount(ShowWinningRecordsParam showWinningRecordsParam) {
        if(showWinningRecordsParam.getActivityId() == null || showWinningRecordsParam.getPrizeId() == null){
            log.error(ServiceErrorCodeConstants.ACTIVITY_OR_ACTIVITY_PRIZE_ISNULL.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.PARAM_ERROR);
        }
        int count = winningRecordMapper.selectByAPId(showWinningRecordsParam.getActivityId(),showWinningRecordsParam.getPrizeId());
        return count;
    }

    @Override
    public void removeRecords(Long activityId, Long prizeId, List<Long> winnerId) {
        if(activityId == null || prizeId == null ){
            log.error(ServiceErrorCodeConstants.PARAM_ERROR.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.PARAM_ERROR);
        }
        winningRecordMapper.deleteRecords(activityId,prizeId);

        removeRecordsToCache(activityId + "_" + prizeId);
    }

    @Override
    public List<WinningRecordDTO> selectWinningRecords(ShowWinningRecordsParam param) {
        if(param.getActivityId() == null || param.getActivityId() == 0){
            log.error(ServiceErrorCodeConstants.PARAM_ERROR.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.PARAM_ERROR);
        }
        List<WinningRecordDO> winningRecordDOList;
        String key = param.getActivityId() + (param.getPrizeId() == null ? "" : "_" + param.getPrizeId());
        if(RedisUtil.hasKey(key)){
            winningRecordDOList = JacksonUtil.readListValue(RedisUtil.get(key), WinningRecordDO.class);
            return convertTo(winningRecordDOList);
        }
        log.info("Redis 中找不到 key:{}",key);

        winningRecordDOList = winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());

        saveRecordsToCache(key,winningRecordDOList,PRIZE_RECORD_EFFECTIVE_TIME);
        return convertTo(winningRecordDOList);
    }

    private List<WinningRecordDTO> convertTo(List<WinningRecordDO> winningRecordDOList) {
        if (winningRecordDOList == null) {
            return List.of();
        }
        List<WinningRecordDTO> winningRecordDTOList = winningRecordDOList.stream()
                .map(
                        wRDO -> {
                            WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                            winningRecordDTO.setWinnerId(wRDO.getWinnerId());
                            winningRecordDTO.setWinnerName(wRDO.getWinnerName());
                            winningRecordDTO.setPrizeName(wRDO.getPrizeName());
                            winningRecordDTO.setPrizeTier(wRDO.getPrizeTier().getTiers());
                            winningRecordDTO.setWinningTime(wRDO.getWinningTime());
                            return winningRecordDTO;
                        }
                ).collect(Collectors.toList());

        return winningRecordDTOList;
    }

    private void removeRecordsToCache(String key) {
        if(!RedisUtil.hasKey(key)){
            log.warn("删除缓存失败，Redis中不存在这个key:{} ",key);
            return;
        }
        try {
            RedisUtil.del(key);
        }catch (Exception e){
            log.error("删除key失败。key:{},  error message ,{}",key,e.getMessage(),e);
        }
    }

    private void saveRecordsToCache(String record,
                                    List<WinningRecordDO> winningRecordDOList,
                                    Long effectiveTime){
        if(!StringUtils.hasText(record) || winningRecordDOList == null || winningRecordDOList.isEmpty()){
            log.error("key：{} 或 value: {}为空!" ,record,winningRecordDOList);
            return;
        }
        if(effectiveTime == null || effectiveTime < 0){
            log.error("中奖结果 缓存时间 有问题！effectiveTime : {}",effectiveTime);
            return;
        }

        try {
            RedisUtil.set(record,JacksonUtil.writeValueAsString(winningRecordDOList),effectiveTime);
            log.info("中奖结果缓存成功！");
        }catch (Exception e){
            log.error("中奖结果 缓存 失败！ e :{e}",e);
        }
    }
}
